package com.comp6231.replica3.client;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.Scanner;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.omg.CORBA.ORB;

import com.comp6231.replica3.common.CriminalRecord;
import com.comp6231.replica3.common.RecordStatus;
import com.comp6231.replica3.common.police_station_idl.StationServerInterface;
import com.comp6231.replica3.common.police_station_idl.StationServerInterfaceHelper;
import com.comp6231.replica3.server.ServerStarter;

/**
 * this is the client class
 * 
 * @author mojtabaebadi
 * 
 */
public class CorbaClient {

	private StationServerInterface spvmServer;
	private StationServerInterface splServer;
	private StationServerInterface spbServer;

	public static void main(String[] args) throws Exception {
		new CorbaClient().start(args);
	}

	/**
	 * this starts the client to run test cases
	 * 
	 * @param args
	 */
	public void start(String[] args) {

		ORB orb = ORB.init(args, null);

		spvmServer = readServerObjects(orb, "spvm");
		splServer = readServerObjects(orb, "spl");
		spbServer = readServerObjects(orb, "spb");

		initLoggers(new String[] { "SPL1111", "SPVM1111", "SPVM1112", "SPB1111" });

		try {

			while (true) {
				char option = getSelectedMenuOption();
				switch (option) {
				case 'q':
				default:
					System.out.println("Session ended.");
					System.exit(0);
					break;
				case '1':
					testCase1();
					break;
				case '2':
					testCase2();
					break;
				case '3':
					testCase3();
					break;
				case '4':
					testCase4();
					break;
				case '5':
					testCase5();
					break;
				case '6':
					testCase6();
					break;
				case '7':
					testCase7();
					break;
				case '8':
					testCase8();
					break;
				case '9':
					testCase9();
					break;
				case '0':
					testCase10();
					break;
				}
				System.out.println();
				System.out
						.println("Operation is done. See log files for results.");
				System.out.println();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	/**
	 * read CORBA object from file
	 * 
	 * @param orb
	 * @param name
	 * @return
	 */
	private StationServerInterface readServerObjects(ORB orb, String name) {
		BufferedReader br;
		try {
			br = new BufferedReader(new FileReader(ServerStarter.baseFolder + "/" + name
					+ ".ior"));
			String ior = br.readLine();
			br.close();

			org.omg.CORBA.Object o = orb.string_to_object(ior);

			return StationServerInterfaceHelper.narrow(o);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * show menu and get an option from user
	 * 
	 * @return
	 */
	private char getSelectedMenuOption() {
		System.out
				.println("Please choose one of the following options or press \"q\" to quit.");
		System.out.println("1- Create a criminal record in SPL");
		System.out.println("2- Create a missing record in SPVM");
		System.out.println("3- 1 & 2 plus get records count from SPVM");
		System.out.println("4- Non-authorized access to SPB");
		System.out.println("5- Get records count from SPVM");
		System.out.println("6- Create two records in SPVM concurrently");
		System.out.println("7- Update a criminal record with wrong status");
		System.out.println("8- Transfer a record from SPL to SPVM");
		System.out.println("9- Transfer and edit records concurrently on SPL");
		System.out
				.println("10- testcase 9 + transfer a record from SPB to SPVM (enter 0)");

		// System.out.println("8- Manual test");

		Scanner consoleReader = new Scanner(System.in);
		while (consoleReader.hasNext()) {
			String input = consoleReader.nextLine();
			if (input.length() > 0) {
				return input.charAt(0);
			}
		}
		return 'q';
	}

	/**
	 * initialize logger
	 * 
	 * @param names
	 */
	private static void initLoggers(String[] names) {
		for (String name : names) {
			Logger log = Logger.getLogger(name);
			FileAppender fa = new FileAppender();
			fa.setName(name);
			fa.setFile(ServerStarter.baseFolder + "/" + name + ".log");
			fa.setLayout(new PatternLayout("%d %m%n"));
			fa.setThreshold(Level.ALL);
			fa.setAppend(false);
			fa.activateOptions();
			log.addAppender(fa);
		}
	}

	/**
	 * get the corresponding server object for an officer
	 * 
	 * @param badgeID
	 * @return
	 */
	private StationServerInterface getServerForOfficer(String badgeID) {
		if (badgeID.startsWith("SPVM")) {
			return spvmServer;
		}
		if (badgeID.startsWith("SPL")) {
			return splServer;
		}
		if (badgeID.startsWith("SPB")) {
			return spbServer;
		}
		return null;
	}

	/**
	 * test case 1
	 */
	private void testCase1() {
		String offID = "SPL1111";
		try {
			Logger.getLogger(offID).info(
					"Requested to create a criminal record in SPL");
			Logger.getLogger(offID).info(
					"John" + " Smith" + " robbery " + RecordStatus.OnTheRun);
			boolean result = getServerForOfficer(offID).createCRecord("John",
					"Smith", "robbery", RecordStatus.OnTheRun.ordinal(), offID);
			if (result) {
				Logger.getLogger(offID)
						.info("Created a criminal record in SPL");
			} else {
				Logger.getLogger(offID).info(
						"Couldn't create a criminal record in SPL");
			}
		} catch (Exception e) {
			Logger.getLogger(offID).error(e.getMessage());
		}
	}

	/**
	 * test case 2
	 */
	private void testCase2() {
		String offID = "SPVM1112";
		try {
			Logger.getLogger(offID).info(
					"Requested to create a missing record in SPVM");
			Logger.getLogger(offID).info(
					"Steve" + " Cohen" + " montreal" + " 2013-09-04"
							+ " concordia university " + RecordStatus.Missing);
			boolean result = getServerForOfficer(offID).createMRecord("Steve",
					"Cohen", "montreal", "2013-09-04", "concordia university",
					RecordStatus.Missing.ordinal(), offID);

			if (result) {
				Logger.getLogger(offID).info("Created a missing record in SPL");
			} else {
				Logger.getLogger(offID).info(
						"Couldn't create a missing record in SPL");
			}

		} catch (Exception e) {
			Logger.getLogger(offID).error(e.getMessage());
		}
	}

	/**
	 * test case 3
	 */
	private void testCase3() {
		String offID = "SPVM1111";
		try {
			testCase1();
			testCase2();

			Logger.getLogger(offID).info(
					"Requested to get records count in SPVM");
			String result = getServerForOfficer(offID).getRecordsCount(
					(short) 0, "SPVM1111");
			Logger.getLogger(offID).info("Received result from SPVM");
			Logger.getLogger(offID).info(result);
		} catch (Exception e) {
			Logger.getLogger(offID).error(e.getMessage());
		}
	}

	/**
	 * test case 4
	 */
	private void testCase4() {
		String offID = "SPL1111";
		try {
			Logger.getLogger(offID).info(
					"Requested to create a criminal record in SPB");
			Logger.getLogger(offID).info(
					"John " + "getServerForOfficer(offID) " + "robbery "
							+ RecordStatus.OnTheRun);
			boolean result = getServerForOfficer(offID).createCRecord("John",
					"Smith", "robbery", RecordStatus.OnTheRun.ordinal(), offID);
			if (result) {
				Logger.getLogger(offID)
						.info("Created a criminal record in SPB");
			} else {
				Logger.getLogger(offID).info(
						"Couldn't create a criminal record in SPB");
			}
		} catch (Exception e) {
			Logger.getLogger(offID).error(e.getMessage());
		}

	}

	/**
	 * test case 5
	 */
	private void testCase5() {
		String offID = "SPVM1112";
		try {
			Logger.getLogger(offID).info("Requested records count from SPVM");
			String result = getServerForOfficer(offID).getRecordsCount(
					(short) 0, "SPVM1112");
			Logger.getLogger(offID).info("Received result from SPVM");
			Logger.getLogger(offID).info(result);
		} catch (Exception e) {
			Logger.getLogger(offID).error(e.getMessage());
		}
	}

	/**
	 * test case 6
	 */
	private void testCase6() {
		String offID1 = "SPVM1111";
		String offID2 = "SPVM1112";
		CreateRecord cr1 = new CreateRecord();
		cr1.offID = offID1;
		cr1.record = new CriminalRecord(null, "David", "Green", "description",
				RecordStatus.Captured);
		CreateRecord cr2 = new CreateRecord();
		cr2.offID = offID2;
		cr2.record = new CriminalRecord(null, "Michael", "Bell", "description",
				RecordStatus.OnTheRun);
		new Thread(cr1).start();
		new Thread(cr2).start();
	}

	/**
	 * create a record on a thread
	 * 
	 * @author mojtabaebadi
	 * 
	 */
	class CreateRecord implements Runnable {

		public String offID;

		public CriminalRecord record;

		@Override
		public void run() {

			try {
				Logger.getLogger(offID).info(
						"Requested to create a criminal record in SPVM");
				Logger.getLogger(offID).info(record);
				boolean result = getServerForOfficer(offID).createCRecord(
						record.firstName, record.lastName, record.description,
						record.status.ordinal(), offID);
				if (result) {
					Logger.getLogger(offID).info(
							"Created a criminal record in SPVM");
				} else {
					Logger.getLogger(offID).info(
							"Couldn't create a criminal record in SPVM");
				}
			} catch (Exception e) {
				Logger.getLogger(offID).error(e.getMessage());
			}

		}

	}

	/**
	 * test case 7
	 */
	private void testCase7() {
		String offID = "SPL1111";
		CriminalRecord r = new CriminalRecord(null, "FirstName2", "MLastName2",
				"desc 2", RecordStatus.OnTheRun);
		try {
			Logger.getLogger(offID).info(
					"Requested to edit a criminal record in SPVM");
			Logger.getLogger(offID).info(
					"MLastName2" + " CR00002 " + RecordStatus.Found);
			boolean result = getServerForOfficer(offID).editCRecord(
					"MLastName2", "CR00002", RecordStatus.Found.ordinal(),
					offID);
			if (result) {
				Logger.getLogger(offID)
						.info("Edited a criminal record in SPVM");
			} else {
				Logger.getLogger(offID).info(
						"Couldn't edit a criminal record in SPVM");
			}
		} catch (Exception e) {
			Logger.getLogger(offID).info(
					"Couldn't edit a criminal record in SPVM.");
		}

	}

	/**
	 * test case 8
	 */
	private void testCase8() {
		String offID = "SPL1111";
		Logger.getLogger(offID).info(
				"requested to transfer a record with ID CR00001 to spvm");
		boolean result = getServerForOfficer(offID).transferRecord(offID,
				"CR00001", "spvm");
		if (result) {
			Logger.getLogger(offID).info(
					"Record transfered to SPVM successfully");
		} else {
			Logger.getLogger(offID).info("Transfering record failed");
		}
	}

	/**
	 * edit and transfer records concurrently
	 */
	private void testCase9() {
		String offID = "SPL1111";

		TransferRecord transferThread = new TransferRecord();
		transferThread.offID = offID;
		transferThread.RecordID = "MR00001";
		transferThread.RemoteServer = "spvm";

		EditRecord editThread = new EditRecord();
		editThread.offID = offID;

		new Thread(transferThread).start();
		new Thread(editThread).start();

	}

	/**
	 * test case 10
	 */
	private void testCase10() {
		String offID1 = "SPL1111";
		String offID2 = "SPB1111";

		TransferRecord transferThread1 = new TransferRecord();
		transferThread1.offID = offID1;
		transferThread1.RecordID = "MR00001";
		transferThread1.RemoteServer = "spvm";

		TransferRecord transferThread2 = new TransferRecord();
		transferThread2.offID = offID2;
		transferThread2.RecordID = "CR00001";
		transferThread2.RemoteServer = "spvm";

		EditRecord editThread = new EditRecord();
		editThread.offID = offID1;

		new Thread(transferThread1).start();
		new Thread(transferThread2).start();
		new Thread(editThread).start();
	}

	/**
	 * Transfer a record on a thread
	 * 
	 * @author mojtabaebadi
	 * 
	 */
	class TransferRecord implements Runnable {

		public String offID;

		public String RecordID;

		public String RemoteServer;

		@Override
		public void run() {
			Logger.getLogger(offID).info(
					"requested to transfer a record with ID " + RecordID
							+ " to " + RemoteServer);
			boolean result = getServerForOfficer(offID).transferRecord(offID,
					RecordID, RemoteServer);
			if (result) {
				Logger.getLogger(offID).info(
						"Record transfered to SPVM successfully");
			} else {
				Logger.getLogger(offID).info("Transfering record failed");
			}
		}

	}

	/**
	 * edit a record on a thread
	 * 
	 * @author mojtabaebadi
	 * 
	 */
	class EditRecord implements Runnable {

		public String offID;

		public String lastName;

		public String recordID;

		public RecordStatus status;

		@Override
		public void run() {
			Logger.getLogger(offID).info(
					"Requested to edit a criminal record in SPL");
			Logger.getLogger(offID).info(
					"LLastName2" + " CR00002 " + RecordStatus.Captured);
			boolean result = getServerForOfficer(offID).editCRecord(
					"LLastName2", "CR00002", RecordStatus.Captured.ordinal(),
					offID);
			if (result) {
				Logger.getLogger(offID).info("Edited a criminal record in SPL");
			} else {
				Logger.getLogger(offID).info(
						"Couldn't edit a criminal record in SPL");
			}

		}

	}
}
